home *** CD-ROM | disk | FTP | other *** search
/ Nebula 1 / Nebula One.iso / Internet / News / Alexandra.0.82 / Source / Newsgroup.m < prev    next >
Encoding:
Text File  |  1996-01-30  |  12.4 KB  |  612 lines

  1. #import "Newsgroup.h"
  2. #import "Article.h"
  3. #import "NNTP.h"
  4. #import "Interval.h"
  5. #import "ColumnMatrix.h"
  6. #include <stdlib.h>
  7. #include <string.h>
  8. #include <stdio.h>
  9. #import "GrayCell.h"
  10. #import "descriptors.h"
  11. #import <ctype.h>
  12. #import "response_codes.h"
  13. #import "rfc822realname.h"
  14. #import "KillFile.h"
  15. #import "Alexandra.h"
  16.  
  17. static int sortType=SORT_BY_NAME;
  18.  
  19. @implementation Newsgroup
  20.  
  21. + setSortType:(int)t
  22. {
  23.     sortType=t;
  24.     
  25.     return self;
  26. }
  27.  
  28. + (int)sortType
  29. {
  30.     return sortType;
  31. }
  32.  
  33. - initTextCell:(const char *)aString
  34. {
  35.    subscribed=FALSE;
  36.    [super initTextCell:aString];
  37.    [self setWrap:NO];
  38.    numUnreadArticles=0;
  39.    articleList=[[MiscSortedList alloc] init];
  40.    [articleList setSortEnabled:FALSE];
  41.    [articleList setSortOrder:Misc_ASCENDING];
  42.  
  43.    intervalList=[[List alloc] init];
  44.    delayed=TRUE;
  45.    min=0;
  46.    max=-1;
  47.     first_unread=0;
  48.     
  49.    return self;
  50. }
  51.  
  52. - updateArticles
  53. {
  54.    int i,j=[articleList count];
  55.     for(i=0;i<j;i++)
  56.         [[articleList objectAt:i] composeTitle];
  57.         
  58.     return self;
  59. }
  60.  
  61. - free
  62. {
  63.    if(articleList!=nil){
  64.       [articleList makeObjectsPerform:@selector(free)];
  65.       [articleList free];
  66.    }
  67.    if(intervalList!=nil){
  68.       [intervalList makeObjectsPerform:@selector(free)];
  69.       [intervalList free];
  70.    }
  71.    [super free];
  72.    return self;
  73. }
  74.  
  75. - setSubscribed
  76. {
  77.    subscribed=TRUE;
  78.    return self;
  79. }
  80.  
  81. - setUnsubscribed
  82. {
  83.    subscribed=FALSE;
  84.    return self;
  85. }
  86.  
  87. - (BOOL)isSubscribed
  88. {
  89.    return subscribed;
  90. }
  91.  
  92. - setNumUnreadArticles:(int)num
  93. {
  94.    numUnreadArticles=(unsigned)num;
  95.    return self;
  96. }
  97.  
  98. - incNumberUnreadArticles:(int)delta
  99. {
  100.    numUnreadArticles+=delta;
  101.    return self;
  102. }
  103.  
  104. - approxNumUnread
  105. {
  106.    long i;
  107.  
  108.    numUnreadArticles=0;
  109.    if((min==0)&&(max==0))
  110.       return self;
  111.    for(i=min;i<=max;i++)
  112.       if([self inReadList:i]==FALSE)
  113.          numUnreadArticles++;
  114.    return self;
  115. }
  116.  
  117. - (long)numberUnreadArticles
  118. {
  119.    return numUnreadArticles;
  120. }
  121.  
  122. - (int)markAllReadUntil:lastArticle
  123. {
  124.    int i,j,ii=0;
  125.    Article *anArticle;
  126.  
  127.    if(delayed==TRUE){
  128.       if(intervalList!=nil){
  129.          [intervalList makeObjectsPerform:@selector(free)];
  130.          [intervalList empty];
  131.       }
  132.       else
  133.          intervalList=[[List alloc] init];
  134.       if(min<=max)
  135.          [intervalList addObject:[[Interval alloc] initWithLower:min upper:max]];
  136.       [self setNumUnreadArticles:0];
  137.    }
  138.    else{
  139.       for(i=0,j=[articleList count];i<j;i++){
  140.          anArticle=[articleList objectAt:i];
  141.          if([anArticle isRead]==FALSE){
  142.             ii++;
  143.             [anArticle setRead];
  144.          }
  145.          if(anArticle==lastArticle)
  146.             break;
  147.       }
  148.       if(ii>0)
  149.          [self incNumberUnreadArticles:(-1)*ii];
  150.    }
  151.  
  152.    return ii;
  153. }
  154.  
  155. - setTextAttributes:textObj
  156. {
  157.    [super setTextAttributes:textObj];
  158.    if (subscribed==FALSE)
  159.       [textObj setTextGray:NX_DKGRAY];
  160.    else
  161.       [textObj setTextGray:NX_BLACK];      
  162.    return textObj;
  163. }
  164.  
  165. - drawInside:(const NXRect *)cellFrame inView:controlView
  166. {
  167.    NXRect numrect;
  168.    static id sharedTextCell = nil;
  169.    char numstr[40];
  170.     
  171.     //erase cell
  172.     PSsetgray((cFlags1.state || cFlags1.highlighted) ? NX_WHITE : NX_LTGRAY);
  173.     NXRectFill(cellFrame);
  174.  
  175.     //make cell
  176.     if (!sharedTextCell) {
  177.         sharedTextCell = [[GrayCell alloc] init];
  178.         [sharedTextCell setWrap:NO];
  179.     }
  180.    
  181.    // draw text
  182.    if(subscribed==TRUE)
  183.       [sharedTextCell setDrawGray:FALSE];
  184.    else
  185.       [sharedTextCell setDrawGray:TRUE];
  186.    [sharedTextCell setFont:[self font]];
  187.    [sharedTextCell setStringValueNoCopy:[self stringValue]];
  188.    [sharedTextCell drawInside:cellFrame inView:controlView];
  189.    
  190.    // draw number unread articles to the right
  191.    if(numUnreadArticles!=0){
  192.       sprintf(numstr,"%d",numUnreadArticles);
  193.       [sharedTextCell setStringValue:numstr];
  194.       NX_WIDTH(&numrect)=[[sharedTextCell font] getWidthOf:numstr]+4.0;
  195.       NX_HEIGHT(&numrect)=NX_HEIGHT(cellFrame);
  196.       NX_X(&numrect)=NX_X(cellFrame)+NX_WIDTH(cellFrame)- NX_WIDTH(&numrect);
  197.       NX_Y(&numrect)=NX_Y(cellFrame);
  198.       PSsetgray((cFlags1.state || cFlags1.highlighted) ? NX_WHITE : NX_LTGRAY);
  199.       NXRectFill(&numrect);
  200.       [sharedTextCell drawInside:&numrect inView:controlView];
  201.    }
  202.  
  203.    PSsetgray(NX_DKGRAY);
  204.    if (cFlags1.state || cFlags1.highlighted){
  205.       NXRect rectArray[2];
  206.  
  207.       NXSetRect(&(rectArray[0]),NX_X(cellFrame),NX_Y(cellFrame), NX_WIDTH(cellFrame),1);
  208.       NXSetRect(&(rectArray[1]),NX_X(cellFrame),NX_MAXY(cellFrame)-1,
  209. NX_WIDTH(cellFrame), 1.0);
  210.       NXRectFillList(rectArray, 2);
  211.    }
  212.  
  213.    return self;
  214. }
  215.  
  216. - highlight:(const NXRect *)cellFrame inView:controlView lit:(BOOL)flag
  217. {
  218.     if(cFlags1.highlighted != flag){
  219.        cFlags1.highlighted = flag;
  220.        [self drawInside:cellFrame inView:controlView];
  221.     }
  222.     return self;
  223. }
  224.  
  225. - setReadList:(char *)readlist
  226. {
  227.    int x,y,i;
  228.    long lower,upper;
  229.    
  230.    if(intervalList==nil)
  231.       intervalList=[[List alloc] init];
  232.    y=strlen(readlist);
  233.    if((y>0)&&(readlist[y-1]=='\n')){
  234.       readlist[y-1]='\0';
  235.       y--;
  236.    }
  237.    i=0;
  238.    while(i<y){
  239.       x=i;
  240.       while((i<y)&&(isdigit(readlist[i])!=0))
  241.          i++;
  242.       if(isdigit(readlist[x])!=0)
  243.          lower=atol(readlist+x);
  244.       else
  245.          lower=0;
  246.       if((i==y)||(readlist[i]==',')){
  247.          upper=lower;
  248.          i++;
  249.       }
  250.       else if(readlist[i]=='-'){
  251.          x=++i;
  252.          while((i<y)&&(isdigit(readlist[i])!=0))
  253.             i++;
  254.          if(isdigit(readlist[x])!=0)
  255.             upper=atol(readlist+x);
  256.          else
  257.             upper=0;
  258.          if(readlist[i]==',')
  259.             i++;
  260.       }
  261.       else{
  262.          i++;
  263.          continue;
  264.       }
  265.       if((upper!=0) && (lower!=0))
  266.          [intervalList addObject:[[Interval alloc] initWithLower:lower upper:upper]];
  267.    }
  268.     
  269.     if([intervalList count]>0 && [NXApp defaultBoolValue:DEFAULT_PRIVATE_EXPIRE])
  270.         first_unread=[[intervalList objectAt:0] upperBound]+1;
  271.     
  272.    return self;
  273. }
  274.  
  275. - (BOOL)inReadList:(long)number
  276. {
  277.    int i,ii;
  278.    
  279.    ii=[intervalList count];
  280.    for(i=0;i<ii;i++)
  281.       if([[intervalList objectAt:i] isIn:number]==TRUE)
  282.          return TRUE;
  283.    return FALSE;
  284. }
  285.  
  286. - dumpReadList:(NXStream *)aStream
  287. {
  288.  
  289.    long smallest,largest;
  290.    BOOL run_started;
  291.    long start,stop;
  292.    long i,j;
  293.    char *bucket;
  294.    long bsize;
  295.    long c=0;
  296.  
  297.    //ever scanned this newsgroup?
  298.    if(delayed==TRUE){
  299.       j=[intervalList count];
  300.       for(i=0;i<j;i++){
  301.          [[intervalList objectAt:i] dumpAsAsciiIn:aStream];
  302.          if(i<j-1)
  303.             NXWrite(aStream,",",1);
  304.       }
  305.       return self;
  306.    }
  307.  
  308.    // no articles in this group?
  309.    j=[articleList count];
  310.    if(j==0){
  311.       if(min<max)
  312.          NXPrintf(aStream,"1-%d",max);
  313.       return self;
  314.    }
  315.  
  316.    //let's start -- first get smallest and largest article number
  317.    smallest=[[articleList objectAt:0] number];
  318.    largest=smallest;
  319.    for(i=0;i<j;i++){
  320.       long num=[[articleList objectAt:i] number];
  321.       if(num<smallest) smallest=num;
  322.       if(num>largest) largest=num;
  323.    }
  324.  
  325.    //malloc bucket and fill it
  326.    bsize=largest-smallest+2;
  327.    bucket=(char *)calloc(bsize,sizeof(char));
  328.    
  329.    for(i=0;i<j;i++){
  330.       id aCell=[articleList objectAt:i];
  331.       if([aCell isRead]==FALSE)
  332.          bucket[[aCell number]-smallest]=(char)1;
  333.    }
  334.    bucket[bsize-1]=(char)1; //dummy!!!
  335.  
  336.    //init
  337.    run_started=FALSE;
  338.    start=1;
  339.    stop=0;
  340.    if(smallest!=1){
  341.       run_started=TRUE;
  342.       stop=smallest-1;
  343.    }
  344.    
  345.    for(i=0;i<bsize;i++){
  346.       if(run_started==TRUE){
  347.          if(bucket[i]==(char)1){
  348.             if(start<=stop)
  349.                if(c>0)
  350.                   NXPrintf(aStream,",");
  351.                else c++;
  352.             if(start<stop)
  353.                NXPrintf(aStream,"%d-%d",start,stop);
  354.             else
  355.                if(start==stop)
  356.                   NXPrintf(aStream,"%d",start);
  357.             run_started=FALSE;
  358.          }
  359.          else // bucket==0
  360.             stop++;
  361.       }
  362.       else
  363.          if(bucket[i]==(char)0){
  364.             start=i+smallest;
  365.             stop=start;
  366.             run_started=TRUE;
  367.          }
  368.    }
  369.  
  370.    free(bucket);
  371.  
  372.    return self;
  373. }
  374.  
  375. - scanArticles:(NNTP *)nntpServer
  376. {
  377.    return [self scanArticles:nntpServer visibleIn:nil];
  378. }
  379.  
  380. - scanArticles:(NNTP *)nntpServer visibleIn:articleView
  381. {
  382.    long oldMin,oldMax;
  383.    long from,to,expired_min=0;
  384.    Article *anArticle;
  385.    int i,j,ii;
  386.    Storage *array;
  387.    subjectDesc *subDesc;
  388.    headerDesc *h;
  389.    char buf[512];
  390.  
  391.     if([NXApp defaultBoolValue:DEFAULT_PRIVATE_EXPIRE]){
  392.         float a=(float)[NXApp defaultIntValue:DEFAULT_EXPIRE_RELATIV_OFFSET]/100;
  393.         int b=[NXApp defaultIntValue:DEFAULT_EXPIRE_ABSOLUTE_OFFSET];
  394.         long y1,y2,y1cnt=first_unread-min;
  395.         y1= (y1cnt < 1)? min : (long)(first_unread - a*y1cnt);
  396.         y1cnt=first_unread-b;
  397.         y2= y1cnt<0 ? min : y1cnt;
  398.         expired_min=MAX(y1,y2);
  399.     }
  400.     
  401.    oldMin=-1; oldMax=-1;
  402.    from=-1; to=-1;
  403.    j=[articleList count];
  404.    if((delayed==TRUE)||(j==0)){
  405.         if(expired_min>0)
  406.             from=expired_min;
  407.         else
  408.           from=min;
  409.       to=max;
  410.    }
  411.    else{
  412.       oldMin=[[articleList objectAt:0] number];
  413.       oldMax=oldMin;
  414.       // find min + max
  415.       for(i=0;i<j;i++){
  416.          long num=[[articleList objectAt:i] number];
  417.          if(num>oldMax) oldMax=num;
  418.          if(num<oldMin) oldMin=num;
  419.       }
  420.  
  421.       // see if there's something to do
  422.       if(max>oldMax){
  423.          from=oldMax+1;
  424.          to=max;
  425.       }
  426.    }
  427.  
  428.     
  429.  
  430.    // get new articles?
  431.    if(from!=-1){
  432.       array=[[Storage alloc] initCount:0 elementSize:sizeof(subjectDesc) description:"{l*}"];
  433.         
  434.       [nntpServer fetchSubjectHeaders:array from:from to:to];
  435.  
  436.       j=[array count];
  437.       for(i=0;i<j;i++){
  438.          subDesc=(subjectDesc *)[array elementAt:(unsigned int)i];
  439.  
  440.             //Assert a Subject header
  441.             if(!subDesc->fieldBody[SUBJECT])
  442.                 subDesc->fieldBody[SUBJECT]=NXCopyStringBuffer("(none)");
  443.             else if(!(*subDesc->fieldBody[SUBJECT])){
  444.                 free(subDesc->fieldBody[SUBJECT]);
  445.                 subDesc->fieldBody[SUBJECT]=NXCopyStringBuffer("(none)");
  446.             }
  447.  
  448.          anArticle=[[Article alloc] initWithNumber:subDesc->number];
  449.          h=[anArticle header];
  450.          for(ii=0;ii<XOVER_COUNT;ii++)
  451.             h->fieldBody[ii]=subDesc->fieldBody[ii];
  452.             free(subDesc->fieldBody);
  453.          [anArticle setSize:subDesc->artsize];
  454.          [anArticle setLines:subDesc->lines];
  455.          [anArticle composeTitle];
  456.             
  457.          if([self inReadList:subDesc->number]==TRUE)
  458.             [[anArticle setRead] unsetTag];
  459.          else
  460.             [[anArticle setUnread] setTag];
  461.          [articleList addObject:anArticle];
  462.       } 
  463.       [array free];
  464.    }
  465.    
  466.    // see if there are any expired articles
  467.    if((delayed==FALSE) && (oldMin!=-1) && (min>oldMin))
  468.       for(i=[articleList count]-1;i>=0;i--)
  469.          if([[articleList objectAt:i] number]<min){
  470.             id bogusCell=[articleList objectAt:i];
  471.             if(articleView!=nil)
  472.                [articleView removeInvalidCell:bogusCell andUpdate:FALSE];
  473.             else
  474.                [[articleList removeObjectAt:i] free];
  475.          }
  476.    //get sort order
  477.    sprintf(buf,"SortType %s",[nntpServer serverName]);
  478.    [self resortIfNeeded:[NXApp defaultIntValue:buf]];
  479.    
  480.    //filter articles
  481.    [[nntpServer killFile] filterArticles:self andReloadMatrix:FALSE];
  482.  
  483.    // calc number unread articles
  484.    numUnreadArticles=0;
  485.    j=[articleList count];
  486.    for(i=0;i<j;i++)
  487.       if([[articleList objectAt:i] isRead]==FALSE)
  488.          numUnreadArticles++;
  489.  
  490.    delayed=FALSE;
  491.    return self;
  492. }
  493.  
  494. - (BOOL)isDelayed
  495. {
  496.    return delayed;
  497. }
  498.  
  499. - (List *)articleList
  500. {
  501.    return articleList;
  502. }
  503. - unsetTag
  504. {
  505.    myTag=0;
  506.    return self;
  507. }
  508.  
  509. - setTag
  510. {
  511.    myTag=1;
  512.    return self;
  513. }
  514.  
  515. - (BOOL)isTaged
  516. {
  517.    return(myTag==1);
  518. }
  519.  
  520. - setMin:(long)newMin
  521. {
  522.     min=newMin;
  523.  
  524.    return self;
  525. }
  526.  
  527. - setMax:(long)newMax;
  528. {
  529.    max=newMax;
  530.    return self;
  531. }
  532.  
  533. - (long)minNumber
  534. {
  535.    return min;
  536. }
  537.  
  538. - (long)maxNumber
  539. {
  540.    return max;
  541. }
  542.  
  543. - (BOOL)bogus
  544. {
  545.    return isBogus;
  546. }
  547.  
  548. - setBogus:(BOOL)b
  549. {
  550.   isBogus=b;
  551.   return self;
  552. }
  553.  
  554. - setPostable:(char)p
  555. {
  556.    postable=p;
  557.    return self;
  558. }
  559.  
  560. - (char)postable
  561. {
  562.    return postable;
  563. }
  564.  
  565. - (BOOL)resortIfNeeded:(int)val
  566. {
  567.    if(![articleList sorted]||(val!=currentSortType)){
  568.         currentSortType=val;
  569.         [Article setSortType:val];
  570.         [articleList unsort];
  571.       [articleList sort];
  572.       return TRUE;
  573.    }
  574.    return FALSE;
  575.  
  576. //MiscCompare protocol implementation
  577.  
  578. - (int)compare:anObject
  579. {
  580.     BOOL t;
  581.     int s;
  582.     
  583.    if(anObject==nil) return 0;
  584.     if(sortType==SORT_BY_NAME)
  585.        return(strcmp([self stringValue],[anObject stringValue]));
  586.     
  587.     t=[anObject isTaged];
  588.     if(t!=[self isTaged])
  589.         return (t? 1 : -1);
  590.     
  591.     if(t){
  592.         s=[anObject state];
  593.         if(s!=[self state])
  594.             return (s==1? 1 : -1);
  595.     }
  596.     
  597.     t=[anObject isSubscribed];
  598.     if(t!=[self isSubscribed])
  599.         return(t? 1 : -1);
  600.     
  601.     t=[anObject isDelayed];    
  602.     if(t!=[self isDelayed])
  603.         return(t? 1 : -1);
  604.         
  605.     return 0;
  606. }    
  607.  
  608.  
  609.  
  610. @end
  611.